/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package servlets;

import auxiliares.Log;
import buscador.Consulta;
import buscador.IBuscador;
import buscador.InfoPalabra;
import indexacion.*;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.*;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.tomcat.util.http.fileupload.FileItem;
import org.apache.tomcat.util.http.fileupload.disk.DiskFileItemFactory;
import org.apache.tomcat.util.http.fileupload.servlet.ServletFileUpload;

/**
 *
 * @author marco
 */
@WebServlet(name = "ControladorBusquedas", urlPatterns = {"/ControladorBusquedas"})
public class ControladorBusquedas extends HttpServlet {

    private boolean isMultipart;
    private File file;
    private ServletContext application = null;

    @Override
    public void init(ServletConfig config) throws ServletException {

        application = config.getServletContext();
    }

    private ArrayList<ArrayList<PesoDocumento>> getPesosTerminos(Consulta consulta, Indice indice, ArrayList<Integer> documentosAfectados) {

        int contador;
        ArrayList<Float> frecuenciasInversas = consulta.getFrecuenciasInversas();
        ArrayList<String> palabrasConsulta = consulta.getTokensConsulta();
        ArrayList<ArrayList<PesoDocumento>> WijCalculados = new ArrayList<>();

        ArrayList<InfoPalabra> ocurrenciasTokenTemp;
        ArrayList<PesoDocumento> WijTemp;
        InfoPalabra ocurrenciaTemp;
        int idDocObservado;
        String token;

        documentosAfectados.clear();
        // Primero Obtenemos los identificadores de los documentos que contienen
        // terminos de la consulta

        for (contador = 0; contador < palabrasConsulta.size(); contador++) {
            token = palabrasConsulta.get(contador);
            ocurrenciasTokenTemp = indice.getOcurrencias(token);
            WijTemp = new ArrayList<>();
            if (null != ocurrenciasTokenTemp) {
                for (int j = 0; j < ocurrenciasTokenTemp.size(); j++) {
                    ocurrenciaTemp = ocurrenciasTokenTemp.get(j);
                    idDocObservado = ocurrenciaTemp.getIdDocumento();
                    if (!documentosAfectados.contains(idDocObservado)) {
                        documentosAfectados.add(idDocObservado);
                        // Log.registrarTexto("/home/marco/Escritorio/BuscadorWEB","documento "+idDocObservado);
                    }
                    WijTemp.add(new PesoDocumento(ocurrenciaTemp, frecuenciasInversas.get(contador)));
                         //  Log.registrarTexto("/home/marco/Escritorio/BuscadorWEB", token+" en documento "+ocurrenciasTokenTemp.get(j).getIdDocumento()+ " pesa "+frecuenciasInversas.get(contador));
                }
            }
            WijCalculados.add(WijTemp);

        }
        return WijCalculados;
    }

    private HashMap<Integer, Float> calcularSimilitudesNormalizadas(ArrayList<Float> pesosConsulta, ArrayList<ArrayList<PesoDocumento>> pesosDocTermino, ArrayList<Integer> documentosAfectados) {

        HashMap<Integer, Float> similitudesDocumentos = new HashMap<>();
        int i, j, k;
        int numPesosTerminoDoc;
        int idDoc, idDocPesosTerminos;
        float Wic, similitudTemp;
        float wICNormalizar = 0, wIJCuadrado;
        int N = pesosConsulta.size(); // Numero de terminos de la consulta
        int numDocs = documentosAfectados.size();
        ArrayList<PesoDocumento> pesosDocumentoI;

        // pesosConsulta    = [ W1   W2    |
        //
        // pesosDocTermino  = | W1j1 W2j8  |
        //                    | W1j2 W2j33 |
        //                    | W1j9 W2j16 |
        //                    | W1j4       |

        for (i = 0; i < N; i++) {
            Wic = pesosConsulta.get(i);
            wICNormalizar = (float) (wICNormalizar + Math.pow(Wic, ((float) 2)));
        }
        wICNormalizar = (float) Math.sqrt(wICNormalizar);

        for (j = 0; j < numDocs; j++) {

            similitudTemp = 0;
            wIJCuadrado = 0;
            idDoc = documentosAfectados.get(j);

            for (i = 0; i < N; i++) {
                Wic = pesosConsulta.get(i);

                pesosDocumentoI = pesosDocTermino.get(i);
                numPesosTerminoDoc = pesosDocumentoI.size();

                for (k = 0; k < numPesosTerminoDoc; k++) {
                    idDocPesosTerminos = pesosDocumentoI.get(k).idDoc;

                    if (idDocPesosTerminos == idDoc) {

                        wIJCuadrado = (float) (wIJCuadrado + Math.pow(pesosDocumentoI.get(k).getPeso(), ((float) 2)));
                        similitudTemp = similitudTemp + (Wic * ((float) pesosDocumentoI.get(k).getPeso()));
                    }
                }
            }

            // Se normaliza el peso
            wIJCuadrado = (float) Math.sqrt(wIJCuadrado);
            // Log.registrarTexto("/home/marco/Escritorio/BuscadorWEB", "Peso no normalizado para documento " + idDoc + " es " + similitudTemp + " sum(wIC2)=" + wICNormalizar + " sum(wIJCuadrado) es " + wIJCuadrado);
            similitudTemp = similitudTemp / (wICNormalizar * wIJCuadrado);
            if (!similitudesDocumentos.containsKey(idDoc)) {
                similitudesDocumentos.put(idDoc, similitudTemp);
            }

        }
        return similitudesDocumentos;
    }

    // <editor-fold defaultstate="collapsed" desc="HttpServlet methods. Click on the + sign on the left to edit the code.">
    /**
     * Handles the HTTP
     * <code>GET</code> method.
     *
     * @param request servlet request
     * @param response servlet response
     * @throws ServletException if a servlet-specific error occurs
     * @throws IOException if an I/O error occurs
     */
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        String idDocumento = (String) request.getParameter("idDoc");

        if (null != idDocumento) {
            IndiceDoc indiceDocumentos = (IndiceDoc) application.getAttribute("INDICEDOC");
            int idDoc = (Integer) Integer.parseInt(idDocumento);
                        
             PrintWriter out = response.getWriter();
             indiceDocumentos.getTextoDocumento(idDoc, out);
        } else {
            String consultaBuscador = request.getParameter("CONSULTA");
            StopWords palabrasVacias = (StopWords) application.getAttribute("STOPWORDS");
            Consulta consultaBuscar = new Consulta(consultaBuscador, palabrasVacias);

            if (consultaBuscar != null) {

                application.setAttribute("CONSULTA", consultaBuscar);
                Indice indice = (Indice)((Indexador) application.getAttribute("INDEXADOR")).getIndice();
                ArrayList<Integer> documentosAfectados = new ArrayList<>();
                ArrayList<Float> pesosConsulta = consultaBuscar.getPesosConsulta(indice);
                ArrayList<ArrayList<PesoDocumento>> Wij = this.getPesosTerminos(consultaBuscar, indice, documentosAfectados);

                if (!documentosAfectados.isEmpty()) {
                    HashMap<Integer, Float> similitudes = calcularSimilitudesNormalizadas(pesosConsulta, Wij, documentosAfectados);
                    Float similitudTemp;

                    List<Integer> claves = new ArrayList<>(similitudes.keySet());
                    List<Integer> clavesOrdenadas = new ArrayList<>(claves.size());
                    List<Float> valores = new ArrayList<>(similitudes.values());
                    List<Float> valoresOrdenados = new ArrayList<>(valores.size());
                    valoresOrdenados.addAll(valores);

                    Collections.sort(valoresOrdenados, Collections.reverseOrder());

                    Object[] valoresSimilOrdenados = valoresOrdenados.toArray();
                    int claveDoc; // Identificador del siguiente documento a mostrar
                    int indiceSimilitud;// Indice del valor de similitud siguiente a mostrar en la lista

                    for (int contador = 0; contador < valoresSimilOrdenados.length; contador++) {
                        similitudTemp = (Float) valoresSimilOrdenados[contador];
                        indiceSimilitud = valores.indexOf(similitudTemp);
                        valores.remove(indiceSimilitud);
                        claveDoc = claves.remove(indiceSimilitud);
                        clavesOrdenadas.add(claveDoc);
                    }
                    application.setAttribute("CLAVESORDENADAS", clavesOrdenadas);
                    application.setAttribute("SIMILITUDESORDENADAS", valoresOrdenados);
                } else {
                    application.setAttribute("SIMILITUDES", null);
                    application.setAttribute("CLAVESORDENADAS", null);
                    application.setAttribute("SIMILITUDESORDENADAS", null);
                }
                response.sendRedirect("buscador.jsp");

            } else {
                response.sendRedirect("buscador.jsp");
            }
        }
    }

    /**
     * Handles the HTTP
     * <code>POST</code> method.
     *
     * @param request servlet request
     * @param response servlet response
     * @throws ServletException if a servlet-specific error occurs
     * @throws IOException if an I/O error occurs
     */
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

     
        isMultipart = ServletFileUpload.isMultipartContent(request);
        response.setContentType("text/html");
        java.io.PrintWriter out = response.getWriter();
        if (!isMultipart) {
            out.println("<html>");
            out.println("<head>");
            out.println("<title>Indexar nuevo documento</title>");
            out.println("</head>");
            out.println("<body>");
            out.println("<p>No se pudo subir el archivo</p>");
            out.println("</body>");
            out.println("</html>");
            return;
        }
        DiskFileItemFactory factory = new DiskFileItemFactory();
        // Tamaño maximo de archivo que se permitira guardar en memoria
        factory.setSizeThreshold(IBuscador.maxTamanoFicheroEnMEM);
        // Directorio donde se guardaran temporalmente los ficheros subidos que excedan el tamaño
        // maximo permitido en memoria
        factory.setRepository(new File(application.getRealPath("/recibidos")));

        // Se crea un nuevo manejador de subidas de ficheros
        ServletFileUpload upload = new ServletFileUpload(factory);
        // maximum file size to be uploaded.
        upload.setSizeMax(IBuscador.maxTamanoNuevoDoc);

        try {
            // Se obtienen los ficheros a parsear
            List fileItems = upload.parseRequest(request);

            Iterator i = fileItems.iterator();

            out.println("<html>");
            out.println("<head>");
            out.println("<title>Indexar nuevo documento</title>");
            out.println("</head>");
            out.println("<body>");
            while (i.hasNext()) {
                FileItem fi = (FileItem) i.next();

                if (!fi.isFormField()) {
                    // Get the uploaded file parameters
                    String fileName = fi.getName();
                    boolean isInMemory = fi.isInMemory();
                    long sizeInBytes = fi.getSize();
                    // Guarda el fichero localmente
                    if (fileName.lastIndexOf("\\") >= 0) {
                        file = new File(IAnalizador.dirDocumentos + System.getProperty("file.separator") 
                                + fileName.substring(fileName.lastIndexOf("\\")));
                    } else {
                        file = new File(IAnalizador.dirDocumentos + System.getProperty("file.separator") 
                                + fileName.substring(fileName.lastIndexOf("\\") + 1));
                    }
                     fi.write( file ) ;
                     out.println("Fichero a indexar: " + file.getName() + "<br> Tama&ntilde;o: "+sizeInBytes/1024+" KBytes"+" <br>");
                     Indexador indexador = (Indexador) application.getAttribute("INDEXADOR");
                 //    out.print(indexador.getNumEntradas()+file.getCanonicalPath());
                     indexador.indexarDocumento(file);
          
         //   Log.registrarTexto("/home/marco/Escritorio/BuscadorWEB", file.getCanonicalPath());
       
                     application.setAttribute("INDEXADOR", indexador);
                }
            }
            out.println("<a href=\"buscador.jsp\">Buscador</a><br>");
            out.println("</body>");
            out.println("</html>");
        } catch (Exception ex) {
            out.print(ex.toString());
           // Log.registrarTexto("/home/marco/Escritorio/BuscadorWEB", ex.toString());
        }
    }

    /**
     * Returns a short description of the servlet.
     *
     * @return a String containing servlet description
     */
    @Override
    public String getServletInfo() {
        return "Short description";
    }// </editor-fold>
}

class PesoDocumento {

    int idDoc;
    float peso;

    public PesoDocumento(InfoPalabra token, float idfToken) {
        // Escalamos la frecuencia con el escalado sublineal
        float tfEscalado = (float) token.getFrecuencia();
        if (0 != tfEscalado) {
            tfEscalado = ((float) 1) + ((float) Math.log10(tfEscalado));
        }
        this.idDoc = token.getIdDocumento();

        this.peso = tfEscalado * idfToken;
        //   Log.registrarTexto("/home/marco/Escritorio/BuscadorWEB", "tf "+tfEscalado+" idf "+idfToken+ " en documento "+idDoc+ " pesa "+peso);
    }

    public float getPeso() {
        return this.peso;
    }

    public boolean esDocumentoJ(int J) {
        return (this.idDoc == J);
    }
}
